package web.servlet;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import web.servlet.dto.DtoFactory;
import data.model.DocumentWithType;
import data.model.Group;
import data.model.Membership;

public class GroupServlet extends AbstractJsonCapableServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        final String action = request.getParameter("action");

        if ("createGroup".equals(action)) {
            createGroup(request, response);
        } else if ("validateGroup".equals(action)) {
            validateGroup(request, response);
        } else if ("myOwnGroup".equals(action)) {
            getMyOwnGroup(request, response);
        } else if ("sendInviteGroup".equals(action)) {
            sendGroupInvitation(request, response);
        } else if ("joinGroup".equals(action)) {
            joinGroup(request, response);
        } else if ("kickGroup".equals(action)) {
            removeFromGroup(request, response);
        } else if ("viewGroupMember".equals(action)) {
            getGroupMember(request, response);
        } else if ("deleteGroup".equals(action)) {
            deleteGroup(request, response);
        } else if ("myGroup".equals(action)) {
            getMyGroup(request, response);
        } else {
            jsonHandler.writeAsJson(DtoFactory.createErrorMessage("Invalid action"), response);
        }
    }

    private void getMyGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final List<Membership> myGroups = dataLayer.findMembershipByUser(request.getParameter("email"));
        jsonHandler.writeAsJson(DtoFactory.createMembershipList(myGroups), response);
    }

    private void deleteGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final String user = request.getParameter("email");
        final String groupId = request.getParameter("groupId");

        final Group group = dataLayer.findGroupById(groupId);
        if (dataLayer.isRelationshipExist(group.getName(), user, "owner")) {
            final List<Membership> memberships = dataLayer.findMembershipByGroup(group.getId());
            dataLayer.deleteObjects(memberships.toArray(new DocumentWithType[memberships.size()]));
            dataLayer.deleteObject(group);

            jsonHandler.writeAsJson(DtoFactory.createMessage("Group deleted"), response);
        } else {
            jsonHandler.writeAsJson(DtoFactory.createErrorMessage("Only owner can delete group"), response);
        }
    }

    private void getGroupMember(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final String groupId = request.getParameter("groupId");

        final List<Membership> memberships = dataLayer.findMembershipByGroup(groupId);

        jsonHandler.writeAsJson(DtoFactory.createMembershipList(memberships), response);
    }

    private void removeFromGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final String user = request.getParameter("user");
        final String groupId = request.getParameter("groupId");

        final Group group = dataLayer.findGroupById(groupId);
        if (dataLayer.isRelationshipExist(group.getName(), user, "member")) {
            final Membership membership = dataLayer.findMembership(group.getName(), user, "member");
            dataLayer.deleteObject(membership);

            jsonHandler.writeAsJson(DtoFactory.createMessage("User removed from group"), response);
        } else {
            jsonHandler.writeAsJson(DtoFactory.createErrorMessage("Cannot delete owner from group"), response);
        }
    }

    private void joinGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final Group group = dataLayer.findGroupById(request.getParameter("groupId"));

        final Membership membership = new Membership();
        membership.setEmail(request.getParameter("guest"));
        membership.setGroupId(group.getId());
        membership.setGroupName(group.getName());
        membership.setRole("member");
        dataLayer.addObject(membership);

        response.sendRedirect(request.getContextPath());
    }

    private void sendGroupInvitation(HttpServletRequest request, HttpServletResponse response) throws IOException {
        sendInvitationEmail(request.getParameter("email"), request.getParameter("guest"), request.getParameter("groupId"));

        jsonHandler.writeAsJson(DtoFactory.createMessage("An invitation email has been sent"), response);
    }

    private void getMyOwnGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        final List<Group> myOwnGroup = dataLayer.findGroupByOwner(request.getParameter("email"));
        jsonHandler.writeAsJson(DtoFactory.createGroupList(myOwnGroup), response);
    }

    private void validateGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        boolean isValid = isGroupUnique(request);
        if (isValid) {
            jsonHandler.writeAsJson(DtoFactory.createMessage("Group is valid"), response);
        } else {
            jsonHandler.writeAsJson(DtoFactory.createErrorMessage("Group is not unique"), response);
        }
    }

    private void createGroup(HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (isGroupUnique(request)) {
            final Group group = new Group();
            group.setName(request.getParameter("groupName"));
            dataLayer.addObject(group);

            final Membership membership = new Membership();
            membership.setEmail(request.getParameter("email"));
            membership.setGroupName(request.getParameter("groupName"));
            membership.setGroupId(group.getId());
            membership.setRole("owner");
            dataLayer.addObject(membership);

            jsonHandler.writeAsJson(DtoFactory.createNewGroupResponse(membership.getGroupId(), membership.getGroupName(), membership.getEmail(), membership
                    .getRole()), response);
        } else {
            jsonHandler.writeAsJson(DtoFactory.createErrorMessage("Group already exist"), response);
        }
    }

    private boolean isGroupUnique(HttpServletRequest request) {
        return !dataLayer.isRelationshipExist(request.getParameter("groupName"), request.getParameter("email"), "owner");
    }

    private void sendInvitationEmail(String owner, String guest, String groupId) {
        final Map<String, String> mailTemplateParams = new HashMap<String, String>();
        mailTemplateParams.put("invitation.url", constructJoinGroupUrl(owner, guest, groupId));
        mailTemplateParams.put("invitation.owner", owner);
        mailTemplateParams.put("invitation.group", groupId);

        emailLayer.sendEmail(guest, "invitation.subject", "invitation.body", mailTemplateParams);
    }

    private String constructJoinGroupUrl(String owner, String guest, String groupId) {
        try {
            final StringBuilder url = new StringBuilder();
            url.append("owner=" + URLEncoder.encode(owner, "UTF-8"));
            url.append("&guest=" + URLEncoder.encode(guest, "UTF-8"));
            url.append("&groupId=" + URLEncoder.encode(groupId, "UTF-8"));
            url.append("&action=" + URLEncoder.encode("joinGroup", "UTF-8"));

            return HOST + "/Group?" + url.toString();
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

}
